//use core::{slice::SlicePattern, usize};

use crate::fs::block::{Block, BlockManager, BLOCK_SIZE};
use alloc::{
    string::{String, ToString},
    sync::Arc,
    vec,
    vec::Vec,
};
use spin::Mutex;

use super::fat::Fat;

pub enum NodeType {
    Dir,
    File,
}

pub struct Fat32INode {
    pub(crate) node_type: NodeType,
    pub(crate) name: String,
    pub(crate) size: usize,
    // 该节点对应的磁盘的块编号，按顺序存放
    // block_ids: Vec<usize>,
    // 块编号太多了，只存放第一个簇的编号
    pub(crate) start_cluster_id: usize,
    pub(crate) fat: Arc<Fat>,
    pub(crate) block_manager: Arc<BlockManager>,
}

impl Fat32INode {
    //pub fn new(node_type: NodeType, name: &str, start_cluster_id: usize) -> Self {
    //    Self {
    //        node_type,
    //        name: name.to_string(),
    //        start_cluster_id,
    //    }
    //}
    pub fn read_all(&self, buf: &mut Vec<u8>) {
        buf.clear();
        match self.node_type {
            NodeType::Dir => {}
            NodeType::File => {
                let mut tmp_buf = [0u8; BLOCK_SIZE];
                let mut cur_cluster_id = self.start_cluster_id;
                // 遍历读取所有簇
                loop {
                    let start_blk_id = self.fat.blocks_offset_to_data
                        + (cur_cluster_id - 2) * self.fat.blocks_per_cluster;
                    // 读取单个簇
                    for id in start_blk_id..(start_blk_id + self.fat.blocks_per_cluster) {
                        self.block_manager.read_block(id, &mut tmp_buf);
                        let res_bytes = self.size - buf.len();
                        if res_bytes > BLOCK_SIZE {
                            buf.extend_from_slice(tmp_buf.as_slice());
                        } else {
                            buf.extend_from_slice(&tmp_buf[..res_bytes]);
                        }
                    }
                    if let Some(n_id) = self.fat.next_cluster_id(cur_cluster_id) {
                        cur_cluster_id = n_id;
                    } else {
                        break;
                    }
                }
            }
        }
    }
    pub fn ls(&self) -> Vec<Fat32INode> {
        match self.node_type {
            NodeType::File => vec![],
            NodeType::Dir => {
                let mut res: Vec<Fat32INode> = vec![];
                let mut cur_cluster_id = self.start_cluster_id;
                loop {
                    let cur_blk_id = self.fat.blocks_offset_to_data
                        + (cur_cluster_id - 2) * self.fat.blocks_per_cluster;
                    // 读取一个簇
                    for i in cur_blk_id..(cur_blk_id + self.fat.blocks_per_cluster) {
                        let mut tmp_blk = Block {
                            id: i,
                            data: [0; BLOCK_SIZE],
                        };
                        self.block_manager.read_block(tmp_blk.id, &mut tmp_blk.data);
                        let nodes = self.parse_node(&tmp_blk);
                        for n in nodes {
                            res.push(n);
                        }
                    }
                    // 遍历簇链
                    if let Some(n_id) = self.fat.next_cluster_id(cur_cluster_id) {
                        cur_cluster_id = n_id;
                    } else {
                        break;
                    }
                }
                res
            }
        }
    }

    fn parse_node(&self, data: &Block) -> Vec<Fat32INode> {
        let mut res: Vec<Fat32INode> = vec![];
        let mut i: usize = 0;
        loop {
            let new_node = data.use_as(i, |entry: &ShortEntry| {
                if entry.is_long()
                    || entry.is_empty()
                    || entry.is_deleted()
                    || entry.start_cluster_id() > self.fat.max_cluster_id
                {
                    None
                } else {
                    Some(Fat32INode {
                        name: String::from_utf8_lossy(&entry.name)
                            .trim_end_matches(char::from(0x20))
                            .to_string(),
                        node_type: (|| {
                            if entry.is_dir() {
                                NodeType::Dir
                            } else {
                                NodeType::File
                            }
                        })(),
                        size: u32::from_le_bytes(entry.file_size) as usize,
                        start_cluster_id: entry.start_cluster_id(),
                        fat: self.fat.clone(),
                        block_manager: self.block_manager.clone(),
                    })
                }
            });
            if let Some(new_node) = new_node {
                res.push(new_node);
            }

            i = i + 32;
            if i >= BLOCK_SIZE {
                break;
            }
        }
        res
    }
}
#[repr(C)]
struct ShortEntry {
    // 名称
    name: [u8; 8],
    // 扩展名
    ext: [u8; 3],
    // 属性
    attr: u8,
    // 系统保留
    reserved: u8,
    create_time_ms: u8,
    create_time: [u8; 2],
    create_date: [u8; 2],
    last_access_date: [u8; 2],
    start_cluster_high: [u8; 2],
    last_modify_time: [u8; 2],
    last_modify_date: [u8; 2],
    start_cluster_low: [u8; 2],
    file_size: [u8; 4],
}
impl ShortEntry {
    pub fn is_dir(&self) -> bool {
        self.attr() == FileAttributes::DIRECTORY
    }
    pub fn attr(&self) -> FileAttributes {
        FileAttributes::from_bits_truncate(self.attr)
    }
    pub fn start_cluster_id(&self) -> usize {
        let low = u16::from_le_bytes(self.start_cluster_low);
        let high = u16::from_le_bytes(self.start_cluster_high);
        let res = ((high as u32) << 16 | low as u32) as usize;
        res
        //((self.start_cluster_high as u32) << 16 | self.start_cluster_low as u32) as usize
    }
    pub fn is_long(&self) -> bool {
        self.attr == 0b00001111
    }
    pub fn is_empty(&self) -> bool {
        self.name[0] == 0b00000000
    }
    pub fn is_deleted(&self) -> bool {
        self.name[0] == 0b11100101
    }
}
bitflags! {
    pub struct FileAttributes: u8 {
        const READ_WRITE = 0b00000000;
        const READ_ONLY  = 0b00000001;
        const HIDDEN     = 0b00000010;
        const SYSTEM     = 0b00000100;
        const VOLUME_ID  = 0b00001000;
        const DIRECTORY  = 0b00010000;
        const ARCHIVE    = 0b00100000;
    }
}
